﻿namespace CommonLibrary.WorkFlow
{
    using CommonLibrary.Models;
    using CommonLibrary.WorkFlow.Enums;
    using CommonLibrary.WorkFlow.Exceptions;
    using System;
    using System.Collections.Generic;
    using System.Data;

    public class WorkFlowManager
    {
        public CommonDatabase Database { get; set; }

        public DataRow FlowObject { get; set; }

        public WorkFlowManager(CommonDatabase db)
        {
            this.Database = db;
        }

        public void Start(
            wf_Workflow flow,
            int dataId,
            int createUserId,
            int createDeptId)
        {
            // 插入索引数据
            this.FlowObject = Database.SetCommand(
                                        "insert into wf_flowobject(flowid, forminstanceid, dataid, createuserid, createdeptid) values(@flowId, @formInstanceId, @dataId, @createUserId, @createDeptId) returning *;",
                                        Database.Parameter("@flowId", flow.Id),
                                        Database.Parameter("@formInstanceId", flow.FormInstanceId),
                                        Database.Parameter("@dataId", dataId),
                                        Database.Parameter("@createUserId", createUserId),
                                        Database.Parameter("@createDeptId", createDeptId))
                                      .ExecuteDataTable().Rows[0];

            InsertTaskObject(flow.Id, null);
        }

        public void Execute(
            int taskObjectId,
            Dictionary<string, object> values)
        {
            var taskObject = DbHelper.GetTaskObject(Database, taskObjectId);            
            this.FlowObject = DbHelper.GetFlowObject(Database, (int)taskObject["flowobjectid"]);
            
            var task = DbHelper.GetTask(Database, (int)taskObject["taskid"]);
            var workflow = CreateWorkFlow((WorkType)task["worktype"]);
            if (WorkResult.Continue == workflow.Execute(taskObject, values))
            {
                InsertTaskObject((int)FlowObject["flowid"], (int)taskObject["stepid"]);
            }
        }

        private void InsertTaskObject(int flowId, int? current)
        {
            var chart = Flowchart.Create(flowId, CheckCondition, GetInput);
            var step = chart.GetNext(current);

            if (step == null)
            {
                this.Success();
            }
            else
            {
                InsertNextTaskObject(chart, step);
            }
        }

        private void InsertNextTaskObject(Flowchart chart, Step step)
        {
            try
            {
                var task = DbHelper.GetTask(this.Database, step.TaskId);
                var workflow = CreateWorkFlow((WorkType)task["worktype"]);
                if (workflow.AddTaskObject(step, task, FlowObject) == WorkResult.Complete)
                {
                    var next = chart.GetNext(step.StepId);
                    if (next == null)
                    {
                        Success();
                        return;
                    }

                    InsertNextTaskObject(chart, next);
                }
            }
            catch (Exception ex)
            {
                if (ex is WorkUserNotFoundException ||
                    ex is GoToNextException)
                {
                    var next = chart.GetNext(step.StepId);
                    if (next == null)
                    {
                        Success();
                        return;
                    }

                    InsertNextTaskObject(chart, next);
                    return;
                }

                throw;
            }
        }
        
        /// <summary>
        /// 审批结束---通过
        /// </summary>
        private void Success()
        {
            DbHelper.UpdateFlowObject(Database, (int)FlowObject["id"], (int)FlowObjectState.Adopt, null);

            try
            {
                MailManager.SendSuccessEmail(
                    Database,
                    (int)FlowObject["createuserid"],
                    (string)DbHelper.GetWorkFlow(Database, (int)FlowObject["flowid"])["name"]);
            }
            catch
            {
            }
        }

        private bool CheckCondition(string condition)
        {
            return Database.SetCommand(
                                condition,
                                Database.Parameter("@flowId", (int)FlowObject["flowid"]),
                                Database.Parameter("@flowObjectId", (int)FlowObject["id"]),
                                Database.Parameter("@dataId", (int)FlowObject["dataid"]),
                                Database.Parameter("@createUserId", (int)FlowObject["createuserid"]))
                           .ExecuteScalar<bool>();
        }

        private string GetInput(string query)
        {
            return Database.SetCommand(
                                query,
                                Database.Parameter("@flowId", (int)FlowObject["flowid"]),
                                Database.Parameter("@flowObjectId", (int)FlowObject["id"]),
                                Database.Parameter("@dataId", (int)FlowObject["dataid"]),
                                Database.Parameter("@createUserId", (int)FlowObject["createuserid"]))
                           .ExecuteScalar<string>();
        }

        private IWorkFlow CreateWorkFlow(WorkType type)
        {
            switch (type)
            {
                case WorkType.Approval:
                    return new ApprovalFlow(this);

                case WorkType.View:
                    return new ViewFlow(this);

                default:
                    throw new ArgumentException("worktype");
            }
        }
    }
}